import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import {
  StageTypeEnum,
  registrationIndexInitValue,
  TeamEventList,
} from 'src/common/constant/enums';
import logger from 'src/common/utils/log';
import { getE2PCount } from 'src/common/utils/matchUtils';
import { EventToPlayer } from 'src/modules/event-to-player/entities/event-to-player.entity';
import { EventToPlayerService } from 'src/modules/event-to-player/event-to-player.service';
import { BindPlayerService } from 'src/modules/event-to-player/services/bingPlayer.service';
import { EventService } from 'src/modules/event/event.service';
import { MatchService } from 'src/modules/match/match.service';
import { CreateStageEventDto } from 'src/modules/stage-event/dto/create-stage-event.dto';
import { CreateStageDto } from 'src/modules/stage/dto/create-stage.dto';
import { Stage } from 'src/modules/stage/entities/stage.entity';
import { StageCacheService } from 'src/modules/stage/stage.cache.service';
import { StageService } from 'src/modules/stage/stage.service';
import { TournamentCacheService } from 'src/modules/tournament/tournament.cache.service';
import { handleElimationStage } from './biz/createElimation';
import { handleRoundStage } from './biz/createRound';
import { assign } from 'lodash';

@Injectable()
export class StageCreateService {
  constructor(
    private readonly eventService: EventService,
    private readonly eventToPlayerService: EventToPlayerService,
    private readonly matchService: MatchService,
    private readonly bingPlayerService: BindPlayerService,
    private readonly tournamentCacheService: TournamentCacheService,
    private readonly stageCacheService: StageCacheService,
    private readonly stageService: StageService,
  ) {}
  async createE2PData(
    type: StageTypeEnum,
    playerCount: number,
    stageOrder: number,
    groupCount?: number,
    eventId?: number,
    stageEvents?: CreateStageEventDto[],
  ) {
    logger.info('createE2PData', type, playerCount, groupCount);
    const e2pCount = getE2PCount(type, playerCount, groupCount);
    logger.info('e2pCount', e2pCount);
    const e2pList: EventToPlayer[] = [];
    const byePlayerCount = e2pCount - playerCount;
    const byePlayerIndexArr = [];
    if (type === StageTypeEnum.Elimination) {
      // 倒数后边的比赛每场安排一个轮空
      while (byePlayerIndexArr.length < byePlayerCount) {
        byePlayerIndexArr.push(e2pCount - byePlayerIndexArr.length * 2 - 1);
      }
    } else if (type === StageTypeEnum.Loop) {
      // 把轮空平均分配到每个group里边
      const playersCountPerGroup = e2pCount / groupCount;
      const byePlayerCountPerGroup = Math.floor(byePlayerCount / groupCount);
      const byePlayerCountPerGroupLeft = byePlayerCount % groupCount;
      for (let i = 0; i < groupCount; i++) {
        const addCount =
          i < groupCount - byePlayerCountPerGroupLeft
            ? byePlayerCountPerGroup
            : byePlayerCountPerGroup + 1;
        for (let j = registrationIndexInitValue; j <= addCount; j++) {
          byePlayerIndexArr.push(
            playersCountPerGroup * i + playersCountPerGroup - j + 1,
          );
        }
      }
    }
    for (let i = registrationIndexInitValue; i <= e2pCount; i++) {
      const e2p = new EventToPlayer();
      e2p.registrationIndex = `${i}`;
      e2p.eventId = eventId;
      e2p.stageOrderInEvent = stageOrder;
      if (byePlayerIndexArr.includes(i)) {
        e2p.player1Name = '轮空';
        e2p.isByePlayer = true;
      }
      e2pList.push(e2p);
      // if (stageEvents && stageEvents.length) {
      //   stageEvents.forEach((stageEvent, index) => {
      //     const subStageE2p = new EventToPlayer();
      //     subStageE2p.registrationIndex = `${i}-${index + 1}`;
      //     subStageE2p.eventId = eventId;
      //     subStageE2p.subTeamMatchType = stageEvent.type;
      //     subStageE2p.stageOrderInEvent = stageOrder;
      //     if (byePlayerIndexArr.includes(i)) {
      //       subStageE2p.player1Name = '轮空';
      //       subStageE2p.isByePlayer = true;
      //     }
      //     e2pList.push(subStageE2p);
      //   });
      // }
    }
    const savedE2PData = await this.eventToPlayerService.saveList(e2pList);
    return savedE2PData;
  }

  async create(createStageDto: Partial<CreateStageDto>) {
    // const entity = this.repository.create(createStageDto);
    const {
      type,
      playerCount,
      roundGroupCount,
      stageEvents,
      // stageOrderInEvent,
    } = createStageDto;


    const event = await this.eventService.findOne(createStageDto.fkEventId);

    createStageDto.createBy = createStageDto.createBy;
    createStageDto.updateBy = createStageDto.updateBy;
    createStageDto.tournamentId = event.tournamentId;
    createStageDto.categoryId = event.categoryId;
    createStageDto.event = event;
    if (
      (createStageDto.playerSourceStageId &&
        !createStageDto.playerSourceType) ||
      (createStageDto.playerSourceType && !createStageDto.playerSourceStageId)
    ) {
      throw new HttpException(
        'playerSourceStageId 和 playerSourceType 必须同时存在或同时不存在',
        HttpStatus.BAD_REQUEST,
      );
    }

    if (createStageDto.playerSourceStageId && createStageDto.playerSourceType) {
      const sourceStage = await this.stageService.findOne(
        createStageDto.playerSourceStageId,
      );
      if (!sourceStage) {
        throw new HttpException(
          `没有找到对应的Stage ${createStageDto.playerSourceStageId}`,
          HttpStatus.INTERNAL_SERVER_ERROR,
        );
      }
    }

    let stageOrderInEvent = 1;
    if (event.stages && event.stages.length) {
      event.stages.sort((a, b) => a.stageOrderInEvent - b.stageOrderInEvent);
      stageOrderInEvent =
        event.stages[event.stages.length - 1].stageOrderInEvent + 1;
    }
    createStageDto.stageOrderInEvent = stageOrderInEvent;

    if (stageEvents && stageEvents.length) {
      stageEvents.forEach((stageEvent, index) => {
        stageEvent.eventOrder = index + 1;
        stageEvent.createBy = createStageDto.createBy;
        stageEvent.updateBy = createStageDto.updateBy;
      });
    }

    let stageInfo = new Stage();
    stageInfo = assign(stageInfo, createStageDto);

    let newStage: Stage;

    if (type === StageTypeEnum.Elimination) {
      // 生成淘汰赛
      const players = await this.createE2PData(
        type,
        playerCount,
        stageOrderInEvent,
        null,
        createStageDto.fkEventId,
        stageEvents,
      );

      newStage = handleElimationStage(
        stageInfo,
        players,
        TeamEventList.includes(event.type),
      );
    } else if (type === StageTypeEnum.Loop) {
      const players = await this.createE2PData(
        type,
        playerCount,
        stageOrderInEvent,
        roundGroupCount,
        createStageDto.fkEventId,
        stageEvents,
      );

      newStage = handleRoundStage(
        stageInfo,
        players,
        TeamEventList.includes(event.type),
      );
    }
    const savedStageInfo = await this.stageService.create(newStage as any);
    // 将stageId 保存到match上边
    const { id: stageId } = savedStageInfo;
    const matchList = [];
    if (newStage.groups) {
      savedStageInfo.groups.forEach((group) => {
        group.matchInfos.forEach((match) => {
          match.stageId = stageId;
          matchList.push(match);
          if (match.subMatchInfos) {
            match.subMatchInfos.forEach((subMatch) => {
              subMatch.stageId = stageId;
              matchList.push(subMatch);
            });
          }
        });
      });
    }
    await this.matchService.saveList(matchList);
    await this.tournamentCacheService.handleTournamentUpdate({
      id: savedStageInfo.tournamentId,
    });
    await this.stageCacheService.handleStageUpdate({ id: savedStageInfo.id });
    return savedStageInfo;
  }
}
